using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Timers;
using Microsoft.SharePoint;

namespace CKS.EBE.Caching
{
	public class SPFileWatcher
	{
		protected System.Threading.ReaderWriterLock _lock;
		protected static System.Threading.ReaderWriterLock _staticLock = new System.Threading.ReaderWriterLock();

		class ItemDetails
		{
			public DateTime LastModified;
			public List<SPFileCacheDependency> Dependencies = new List<SPFileCacheDependency>();
		}

		private static SPFileWatcher _current;
		private string _siteUrl;

		private Timer _timer;
		private Dictionary<string, ItemDetails> _items;

		public SPFileWatcher(string siteUrl)
		{
			_items = new Dictionary<string, ItemDetails>();
			_siteUrl = siteUrl.ToLower();
			_lock = new System.Threading.ReaderWriterLock();
		}

		private Dictionary<string, ItemDetails> Items
		{
			get { return _items; }
			set { _items = value; }
		}

		public static SPFileWatcher Current(string siteUrl)
		{
			try
			{
				if (_current == null)
				{
					_staticLock.AcquireWriterLock(-1);
					if (_current == null)
						_current = new SPFileWatcher(siteUrl);
				}
			}
			finally
			{
				if (_staticLock.IsWriterLockHeld)
					_staticLock.ReleaseWriterLock();
			}

			return _current;
		}

		public void AddDepedentItem(SPFileCacheDependency owner, string urlOfItem)
		{
			try
			{
				_lock.AcquireWriterLock(-1);

				if (!Items.ContainsKey(urlOfItem.ToLower()))
				{
					ItemDetails details = new ItemDetails();
					details.LastModified = DateTime.UtcNow;
					details.Dependencies.Add(owner);
					Items.Add(urlOfItem.ToLower(), details);
				}
				else
				{
					ItemDetails details = Items[urlOfItem.ToLower()];
					details.Dependencies.Add(owner);
				}

				_lock.ReleaseWriterLock();

				EnsureStarted();
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex);
			}
			finally
			{
				if (_lock.IsWriterLockHeld)
					_lock.ReleaseWriterLock();
			}
		}

		public void RemoveDepedentItem(SPFileCacheDependency owner, string urlOfItem)
		{
			try
			{
				_lock.AcquireWriterLock(-1);

				if (!Items.ContainsKey(urlOfItem)) return;

				ItemDetails details = Items[urlOfItem];
				if (details.Dependencies.Contains(owner)) details.Dependencies.Remove(owner);

				if (details.Dependencies.Count == 0) Items.Remove(urlOfItem.ToLower());

				if (Items.Count == 0)
				{
					_timer.Stop();
					_timer.Dispose();
					_timer = null;
				}
			}
			finally
			{
				if (_lock.IsWriterLockHeld)
					_lock.ReleaseWriterLock();
			}
		}

		protected void EnsureStarted()
		{
			if (_timer == null)
			{
				_timer = new Timer(3000);
				_timer.AutoReset = true;
				_timer.Elapsed += Timer_Elapsed;
				_timer.Start();
			}
		}

		void Timer_Elapsed(object sender, ElapsedEventArgs e)
		{
			SPSite oSite = null;
			try
			{
				_lock.AcquireWriterLock(4000);

				oSite = new SPSite(_siteUrl.Replace("https:", "http:"));

				string[] arKeys = new string[Items.Keys.Count];
				Items.Keys.CopyTo(arKeys, 0);

				for (int c = Items.Count - 1; c >= 0; c--)
				{
					string sKey = arKeys[c];
					try
					{
						SPChangeQuery q = new SPChangeQuery(false, false);
						q.File = true;
						q.Update = true;


						SPFile oFile = oSite.RootWeb.GetFile(sKey);
						oFile = oFile.ParentFolder.ParentWeb.GetFile(sKey);

						ItemDetails details = Items[sKey];

						if (!oFile.Exists || details.LastModified < oFile.TimeLastModified)
						{
							DependenciesChanged(sKey, details);
						}
					}
					catch (Exception ex)
					{
						Trace.WriteLine(ex);
					}
				}
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex);
			}
			finally
			{
				if (oSite != null) oSite.Close();

				_lock.ReleaseLock();
			}
		}

		private void DependenciesChanged(string sKey, ItemDetails details)
		{
			Trace.WriteLine("Raising dependency changed: " + sKey);
			SPFileCacheDependency[] copy = new SPFileCacheDependency[details.Dependencies.Count];
			details.Dependencies.CopyTo(copy);
			foreach (SPFileCacheDependency d in copy)
			{
				d.RaiseChangedEvent(this, new SPFileCacheEventArgs());
			}
		}
	}
}